package SMUVE;
import java.net.*;
import java.io.*;
import java.util.*;
import SMUVE.*;
import static SMUVE.Debug.*;

public class SMUVE_Server {

	ServerSocket listenSocket;
	Socket connection;

	OutputStream outStream;
	//ObjectOutputStream outObjectStream;
	//ObjectInputStream in;

	DataManager dm=new DataManager();
	ObjectList<Server_Thread> threads = new ObjectList<Server_Thread>();

	Update_Manager update_manager;
	static final String delimiter = SMUVE.Msg_Pipe.delimiter;


	public void runServer() {
		String message;
		boolean done = false;
		Server_Thread[] server_threads;

		rooms.add(new Room("Den", "The Den"));
		rooms.add(new Room("Zoo", "Where the wild things are"));
		players.add(new Player("Norm", "Den"));
		try {
			listenSocket = new ServerSocket(8901);
			System.out.print("Server started");

			try {
				InetAddress here = InetAddress.getLocalHost();
				String host = here.getHostName();
				System.out.println(" on " + host + ", port 8901");
			} catch (UnknownHostException e) {
				System.out.println("Problem with local host\n");
			}

			/* Start a thread that listens for client connections from
			 * clients wishing to receive updates.
			 */
			update_manager = new Update_Manager();
			update_manager.start();

			while (!done) {
				connection = listenSocket.accept();
				System.out.println("Connection request received");

				Server_Thread thd = new Server_Thread(connection);
				thd.start();
				threads.add(thd);
			}
			System.out.println("Shutting down Server");
		} catch (IOException e) {
			//e.printStackTrace();
		} catch (Exception e) {
			System.out.println("Exception");
		}
	}

	public class InputThread extends Thread {
		boolean done = false;
		Msg_Pipe pipe;
		private static final String delimiter = Msg_Pipe.delimiter;

		public InputThread (Msg_Pipe pipe)
		{
			this.pipe = pipe;
		}

		public void run() {
			try {
				/* Here is where messages from the client are processed */
				/* XXX: this needs to be thought through more thouroughly.
				 * Should messages received be translated into function calls?
				 */
				while (pipe != null && !done) {
					String msg = pipe.recv_message();
					String type = Msg_Pipe.get_message_type(msg);
					String body = Msg_Pipe.get_message_body(msg);
					debug("Got message: " + msg);
					if (type.equals("shutdown")) {
						done = true;
						update_manager.update_notify("server is shutting down");
						Thread.sleep(5000);
						System.exit(0);
					}
					else if (type.equals("add_room")) {
						addRoom(body, pipe);
						}
					else if (type.equals("add_portal")) {
						addPortal(body, pipe);
					}
					else if (type.equals("get_portals")) {
						getRoomPortals(body, pipe);
					}
					else if (type.equals("add_player")) {
						addPlayer(body, pipe);	
					}
					else if (type.equals("move")) {
						move(body, pipe);
					}
					else if (type.equals("get_players")) {
						getPlayers(body, pipe);
					}
					else if (type.equals("get_room_players")) {
						getRoomPlayers(body, pipe);
					}
					else if (type.equals("get_current_room")) {
						getCurrentRoom(body, pipe);
					}
					else if (type.equals("get_rooms")) {
						getRooms(body, pipe);
					}
					else if (type.equals("get_player")) {
						getPlayers(body, pipe);
					}
					else if (type.equals("add_item")) {
						addItem(body, pipe);
					}
					else if (type.equals("remove_player")) {
						removePlayer(body, pipe);
					}
					else if (type.equals("remove_room")) {
						removeRoom(body, pipe);
					}
					else if (type.equals("remove_item")) {
						removeItem(body, pipe);
					}
					else {
						debug("Unknown message type: " + type);
						pipe.send_message("unknown message type: " + type);
					}
					Thread.sleep(100);
				}

				//in.close();
				pipe.close();
			}
			catch (EOFException e) {
			}
			catch (IOException e) {
			}
			catch (InterruptedException e) {
				System.out.println("Thread Interrupted");
			}
			catch (Exception e) {
				System.out.println("Caught Exception");
				e.printStackTrace();
			}
		}
	}

	public class Update_Manager extends Thread {
		ServerSocket update_socket;
		Socket sock;
		ObjectList<Update_Thread> subscribers = new ObjectList<Update_Thread>();

		public void run() {
			try {
				update_socket = new ServerSocket(8902);
				debug("Update_Manager started");
				while (true) {
					connection = update_socket.accept();
					Update_Thread thd = new Update_Thread(connection);
					subscribers.add(thd);
					thd.start();
					debug("Started Update_Thread");
					Thread.sleep(100);
				}
			}
			catch (InterruptedException e) {
				System.out.println("Thread Interrupted");
			}
			catch (IOException e) {
				System.out.println("Update_Manager.run: caught IOException");
				e.printStackTrace();
			}
		}

		public void update_notify(String msg) {
			for (int i = 0; i < subscribers.size(); i++) {
				subscribers.get(i).update_notify(msg);
			}
		}
	}

	public class Update_Thread extends Thread {
		Socket sock;
		Msg_Pipe update_pipe;
		ObjectList<Msg_Pipe> subscribers = new ObjectList<Msg_Pipe>();

		public Update_Thread(Socket connection) {
			sock = connection;
			try {
				update_pipe = new Msg_Pipe(sock);
			}
			catch (IOException e) {
				System.out.println("Update_Thread: caught IOException");
				e.printStackTrace();
			}
		}

		public void run() {
			try {
				/* loop forever */
				while (update_pipe != null)
					Thread.sleep(100);

				update_pipe.close();
			}
			catch (InterruptedException e) {
				System.out.println("Thread interrupted");
			}
			catch (Exception e) {
				System.out.println("Exception");
				e.printStackTrace();
			}
		}

		/* XXX: Consider how the clients will receive messages.  There needs
		 * to be synchronization for a send_recv_message so that paired messages
		 * don't get interrupted by one-shot notify messages.
		 */
		public void update_notify(String msg) {
			try {
				update_pipe.send_message(msg);
			}
			catch (Exception e) {
				System.out.println("Exception");
			}
		}
	}

   public class Server_Thread extends Thread {
		Socket connection;
		Socket update_conn;
		Msg_Pipe pipe;

		public Server_Thread(Socket conn) throws Exception {
			connection = conn;
			pipe = new Msg_Pipe(connection);
		}

		public void run() {
			try {
				InputThread monitor = new InputThread(pipe);
				monitor.start();
				debug("monitor started");
			}
			catch (Exception e) {
				System.out.println("Exception");
			}
			
		}
	}
   
   //Start of roddy methods
   private void addRoom(String body, Msg_Pipe pipe)
   {
	   try {
			Room room = Room.fromString(body);
			if(dm.room_exists(room.get_name()))
			{
				pipe.send_message("Room name already exists");
				return;
			}
			if (dm.add_room(room)) {
				debug("sending response");
				pipe.send_message("add_room_success");
				update_manager.update_notify("room_added" +
					delimiter + room);
			}
			else {
				pipe.send_message("add_room_fail");
				debug("add_room failed");
			}
		}
		catch (InvalidMessageException e) {
			pipe.send_message("add_room_fail" + delimiter +
				e.getMessage());
		}
   }
   
   private void addPlayer(String body, Msg_Pipe pipe)
	{
		try {
			Player player = Player.fromString(body);
			if(dm.player_exists(player.get_name()))
			{
				pipe.send_message("Player name already exists");
				return;
			}
			if (dm.add_player(player)) {
				pipe.send_message("add_player_success");
				update_manager.update_notify("player_added" +
					delimiter + player);
			}
			else
				debug("add_player failed");
		}
		catch (InvalidMessageException e) {
			//Changed InvalidMessageException to Exception
			pipe.send_message("add_player_fail" + delimiter + 
				e.getMessage());
		}
	}
   
   private void addItem(String body, Msg_Pipe pipe) {
		try {
			Item item = Item.fromString(body);
			if(dm.item_exists(item.get_name()))
			{
				pipe.send_message("Item name already exists");
				return;
			}
			if (dm.add_item(item)) {
				debug("sending response");
				pipe.send_message("add_item_success");
				update_manager.update_notify("item_added" +
					delimiter + item);
			}
			else {
				pipe.send_message("add_item_fail");
				debug("add_item failed");
			}
		}
		catch (InvalidMessageException e) {
			pipe.send_message("add_item_fail" + delimiter +
				e.getMessage());
		}
	}
   //needs work
   private void addPortal(String body, Msg_Pipe pipe)
   	{
	   if (dm.add_portal(body)) {
		   pipe.send_message("add_portal_success");
		   update_manager.update_notify("add_portal" + delimiter +
				   body);
	   }
	   else
		   pipe.send_message("failure");
   	}
   
   //shit
   private void getRoomPortals(String body, Msg_Pipe pipe)
   	{
	   String resp = "";
		if (dm.room_exists(body)) {
			Room r = dm.get_room(body);
			resp = "get_portals_success" + delimiter +
			r.get_portals();
		}
		else
			resp = "failure";
			pipe.send_message(resp);
}

	
	private void move(String body, Msg_Pipe pipe)
	{
		String[] result = body.split("\\" + delimiter);
		if (result.length != 2)
			throw(new InvalidMessageException("Invalid move message"));
		String room = result[1];
		String player = result[0];
		if (dm.move(player, room)) {
			pipe.send_message("success");
			update_manager.update_notify("player_arrived" +
					delimiter + room + delimiter + player);
		}
		else
			pipe.send_message("failure");
	}
	/*
	private void getPlayers(String body, Msg_Pipe pipe) {
		String resp = "";
		for (int i = 0; i < players.size(); i++) {
			resp += players.get(i).get_name() + delimiter;
		}
		pipe.send_message("get_player_success" + delimiter + resp);
	}
	*/

	//possible disaster
	private void getRoomPlayers(String body, Msg_Pipe pipe) {
		String resp = "";
		Room room= room.fromString(body));
		if (dm.room_exists(body)) {
			resp += "get_room_players_success" + delimiter
					+ dm.get_room_players(room);
		} else
			resp += "failure";

		pipe.send_message(resp);
	}

	private void getCurrentRoom(String body, Msg_Pipe pipe) {
		String room = dm.get_current_room(body);
		if (!room.equals("")) {
			pipe.send_message("get_current_room_success" + delimiter + room);
		} else
			pipe.send_message("failure");
	}
/* Scott?
	private void getRooms(String body, Msg_Pipe pipe) {
		String resp = "";
		for (int i = 0; i < rooms.size(); i++) {
			resp += rooms.get(i).get_name() + delimiter;
		}
		pipe.send_message("get_rooms_success" + delimiter + resp);
	}
*/
	private void getPlayer(String body, Msg_Pipe pipe) {

		Player p;
		if ((p = dm.get_player(body)) != null)
			pipe.send_message("get_player_success" + delimiter + p);
		else
			pipe.send_message("get_player_fail");
	}
	//newness
	
	private void removePlayer(String body, Msg_Pipe pipe) {
		try {
			Player player = Player.fromString(body);
			if (dm.remove_player(player)) {
				pipe.send_message("remove_player_success");
				update_manager.update_notify("player_removed" +
					delimiter + player);
			}
			else
				debug("remove_player failed");
		}
		catch (InvalidMessageException e) {
			//Changed InvalidMessageException to Exception
			pipe.send_message("remove_player_fail" + delimiter + 
				e.getMessage());
		}
	}
	private void removeItem(String body, Msg_Pipe pipe) {
		try {
			Item item = Item.fromString(body);
			if (dm.remove_item(item)) {
				pipe.send_message("remove_item_success");
				update_manager.update_notify("item_removed" +
					delimiter + item);
			}
			else
				debug("remove_item failed");
		}
		catch (InvalidMessageException e) {
			//Changed InvalidMessageException to Exception
			pipe.send_message("remove_item_fail" + delimiter + 
				e.getMessage());
		}
	}
	private void removeRoom(String body, Msg_Pipe pipe) {
		try {
			Room room = Room.fromString(body);
			if (dm.remove_room(room)) {
				pipe.send_message("remove_room_success");
				update_manager.update_notify("room_removed" +
					delimiter + room);
			}
			else
				debug("remove_room failed");
		}
		catch (InvalidMessageException e) {
			//Changed InvalidMessageException to Exception
			pipe.send_message("remove_room_fail" + delimiter + 
				e.getMessage());
		}
	}
	//end Roddy stuff
	public static void main(String [] args) {
		SMUVE_Server server = new SMUVE_Server();

		server.runServer();
	}
}
